package org.mx.service.server;

import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.server.WebSocketHandler;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
import org.mx.StringUtils;
import org.mx.service.server.jmx.WebsocketServer;
import org.mx.service.server.websocket.EchoWebsoket;
import org.mx.service.server.websocket.SimpleWsObject;
import org.mx.service.server.websocket.WsLifeCircle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;

/**
 * 基于Jetty实现的高度定义的Websocket服务器
 *
 * @author : john.peng created on date : 2017/11/4
 */
public class WebsocketServerFactory extends HttpServerFactory {
	private static final Logger logger = LoggerFactory.getLogger(WebsocketServerFactory.class);

	private Map<String, SimpleWsObject> socketBeans;
	private WebsocketServerConfigBean websocketServerConfigBean;

	/**
	 * 默认的构造函数
	 *
	 * @param context                   Spring IoC上下文
	 * @param websocketServerConfigBean WebSocket服务配置对象
	 * @param websocketServerMXBean Websocket服务器JMX MXBean对象
	 */
	public WebsocketServerFactory(ApplicationContext context, WebsocketServerConfigBean websocketServerConfigBean,
			WebsocketServer websocketServerMXBean) {
		super(context, websocketServerConfigBean, websocketServerMXBean);
		this.socketBeans = new HashMap<>();
		this.websocketServerConfigBean = websocketServerConfigBean;
	}

	/**
	 * {@inheritDoc}
	 *
	 * @see AbstractServerFactory#destroy()
	 */
	@Override
	public void destroy() {
		super.destroy();
	}

	/**
	 * {@inheritDoc}
	 *
	 * @see HttpServerFactory#getHandler()
	 */
	@Override
	protected Handler getHandler() {
		String[] classesDefs = websocketServerConfigBean.getServiceClasses();
		String[] basePackage = websocketServerConfigBean.getResourceBasePackages();
		List<Class<?>> resourceClasses = super.getResourceClasses(classesDefs, basePackage, clazz -> {
			boolean result = WsLifeCircle.class.isAssignableFrom(clazz); // clazz.isAssignableFrom(WsLifeCircle.class);
			if (result) {
				if (logger.isDebugEnabled()) {
					logger.debug(
							String.format("The WebSocket resource['%s'] is loaded successfully.", clazz.getName()));
				}
			} else {
				if (logger.isWarnEnabled()) {
					logger.warn(String.format("The class['%s'] is not a WebSocket resource.", clazz.getName()));
				}
			}
			return result;
		});

		if (resourceClasses.isEmpty()) {
			if (logger.isWarnEnabled()) {
				logger.warn("There are not any websocket service be defined, will not start the Http Server.");
			}
			return null;
		} else {
			if (websocketServerConfigBean.isEchoEnabled()) {
				// 根据需要加入测试用ECHO
				WsLifeCircle wsEcho = new EchoWebsoket();
				socketBeans.put(wsEcho.getPath(), new SimpleWsObject(wsEcho));
			}
			resourceClasses.forEach(clazz -> {
				WsLifeCircle ws = (WsLifeCircle) context.getBean(clazz);
				socketBeans.put(ws.getPath(), new SimpleWsObject(ws));
			});
			return new WebSocketHandler() {
				@Override
				public void configure(WebSocketServletFactory factory) {
					WebSocketPolicy policy = factory.getPolicy();
					policy.setIdleTimeout(websocketServerConfigBean.getIdleTimeoutSecs() * 1000);
					policy.setAsyncWriteTimeout(websocketServerConfigBean.getAsyncWriteTimeoutSecs() * 1000);
					policy.setInputBufferSize((int) websocketServerConfigBean.getInputBufferSize());
					policy.setMaxTextMessageSize((int) websocketServerConfigBean.getMaxTextMessageSize());
					policy.setMaxTextMessageBufferSize((int) websocketServerConfigBean.getMaxTextMessageBufferSize());
					policy.setMaxBinaryMessageSize((int) websocketServerConfigBean.getMaxBinaryMessageSize());
					policy.setMaxBinaryMessageBufferSize(
							(int) websocketServerConfigBean.getMaxBinaryMessageBufferSize());
					factory.setCreator(new MyWebsocketCreator());
				}
			};
		}
	}

	/**
	 * Websocket创建器
	 */
	public class MyWebsocketCreator implements WebSocketCreator {
		@Override
		public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp) {
			URI requestUri = req.getRequestURI();
			if (requestUri != null) {
				String path = requestUri.getPath();
				if (!StringUtils.isBlank(path)) {
					// 如果path没有以"/"开头，则拼上
					if (!path.startsWith("/")) {
						path = String.format("/%s", path);
					}
					return socketBeans.get(path);
				}
			}
			return null;
		}
	}
}
